use apikit::rpc::{ResponseParamSign, http_client::FromResponse};
use async_trait::async_trait;
use reqwest::Response;
use serde::de::DeserializeOwned;
use serde_json::Value;
use thiserror::Error;

use crate::{
    dto::Empty,
    param::{__TypeMapper, StreamResult},
};

pub struct TypedResponse<T> {
    pub status: u16,
    pub origin: String,
    pub code: String,
    pub data: Result<T, Empty>,
    pub message: String,
}

impl<T: DeserializeOwned, E> ResponseParamSign<Result<T, E>> for __TypeMapper {
    type Output = TypedResponse<T>;
}

impl ResponseParamSign<StreamResult> for __TypeMapper {
    type Output = Response;
}

#[derive(Debug, Error)]
#[error("{kind}")]
pub struct Error {
    pub status: u16,
    pub origin: String,
    pub kind: ErrorKind,
}

#[derive(Debug, Error)]
pub enum ErrorKind {
    #[error(transparent)]
    ReqwestError(#[from] reqwest::Error),

    #[error(transparent)]
    SerdeError(#[from] serde_json::Error),

    #[error("响应结构错误")]
    StructureError,
}

impl Error {
    fn new(status: u16, kind: ErrorKind) -> Self {
        Self {
            status,
            origin: String::default(),
            kind,
        }
    }
}

#[cfg(not(target_arch = "wasm32"))]
#[async_trait]
impl<T: DeserializeOwned> FromResponse<Response> for TypedResponse<T> {
    type Error = Error;

    async fn from_response(response: Response) -> Result<Self, Self::Error> {
        let status = response.status().as_u16();
        let origin = response
            .text()
            .await
            .map_err(|err| Error::new(status, ErrorKind::ReqwestError(err)))?;

        let value: Value = serde_json::from_str(&origin)
            .map_err(|err| Error::new(status, ErrorKind::SerdeError(err)))?;

        let Value::Object(mut value) = value else {
            return Err(Error::new(status, ErrorKind::StructureError));
        };

        let code = value
            .remove("code")
            .and_then(|v| v.as_str().map(ToString::to_string))
            .unwrap_or_default();

        let message = value
            .remove("message")
            .and_then(|v| v.as_str().map(ToString::to_string))
            .unwrap_or_default();

        let data = if code == "0" {
            let data_value = value.remove("data").unwrap_or_default();
            Ok(serde_json::from_value(data_value)
                .map_err(|err| Error::new(status, ErrorKind::SerdeError(err)))?)
        } else {
            Err(Empty)
        };

        Ok(Self {
            origin,
            status,
            data,
            code,
            message,
        })
    }
}

#[cfg(target_arch = "wasm32")]
#[async_trait(?Send)]
impl<T: DeserializeOwned> FromResponse<Response> for TypedResponse<T> {
    type Error = Error;

    async fn from_response(response: Response) -> Result<Self, Self::Error> {
        let status = response.status().as_u16();
        let origin = response
            .text()
            .await
            .map_err(|err| Error::new(status, ErrorKind::ReqwestError(err)))?;

        let value: Value = serde_json::from_str(&origin)
            .map_err(|err| Error::new(status, ErrorKind::SerdeError(err)))?;

        let Value::Object(mut value) = value else {
            return Err(Error::new(status, ErrorKind::StructureError));
        };

        let code = value
            .remove("code")
            .and_then(|v| v.as_str().map(ToString::to_string))
            .unwrap_or_default();

        let message = value
            .remove("message")
            .and_then(|v| v.as_str().map(ToString::to_string))
            .unwrap_or_default();

        let data = if code == "0" {
            let data_value = value.remove("data").unwrap_or_default();
            Ok(serde_json::from_value(data_value)
                .map_err(|err| Error::new(status, ErrorKind::SerdeError(err)))?)
        } else {
            Err(Empty)
        };

        Ok(Self {
            origin,
            status,
            data,
            code,
            message,
        })
    }
}
